package com.org.devg.elasticsearch.wapper;

import java.util.*;

public class QueryWapper {

    private Map<String, Object> resultMapper = new HashMap<>(initialCapacity);

    private static final int initialCapacity = 3;

    public static QueryWapper put(String name, Object value) {
        QueryWapper queryWapper = new QueryWapper();
        queryWapper.resultMapper.put(name, value);
        return queryWapper;
    }

    public static QueryWapper term(String field, Object value) {
        Map<String, Object> term = new HashMap<>(initialCapacity);
        term.put(field, value);
        return put("term", term);
    }

    public static QueryWapper terms(String field, Collection<?> value) {
        Map<String, Object> term = new HashMap<>(initialCapacity);
        term.put(field, value);
        return put("terms", term);
    }

    public static QueryWapper exists(String field) {
        Map<String, Object> term = new HashMap<>(initialCapacity);
        term.put("field", field);
        return put("exists", term);
    }

    public static QueryWapper must(QueryWapper... value) {
        List<Map<String, Object>> list = new ArrayList<>();
        for (QueryWapper queryWapper : value) {
            list.add(queryWapper.resultMapper);
        }
        return put("must", list);
    }

    public static QueryWapper should(QueryWapper... value) {
        List<Map<String, Object>> list = new ArrayList<>();
        for (QueryWapper queryWapper : value) {
            list.add(queryWapper.resultMapper);
        }
        return put("should", list);
    }

    public static QueryWapper match(String field, Object value) {
        Map<String, Object> map = new HashMap<>(initialCapacity);
        map.put(field, value);
        return put("match", map);
    }

    public static QueryWapper matchPhrase(String field, Object value) {
        Map<String, Object> map = new HashMap<>(initialCapacity);
        map.put(field, value);
        return put("match_phrase", map);
    }

    public static QueryWapper mutiMatch(String query, List<String> fields) {
        Map<String, Object> map = new HashMap<>(initialCapacity);
        map.put("query", query);
        map.put("fields", fields);
        return put("multi_match", map);
    }

    public static QueryWapper prefix(String field, Object value) {
        Map<String, Object> map = new HashMap<>(initialCapacity);
        map.put(field, value);
        return put("prefix", map);
    }

    public static QueryWapper matchQueryOperator(String field, String query, String operator) {
        Map<String, Object> fieldMap = new HashMap<>(initialCapacity);
        fieldMap.put("query", query);
        fieldMap.put("operator", operator);
        Map<String, Object> map = new HashMap<>(initialCapacity);
        map.put(field, fieldMap);
        return put("match", map);
    }

    public static QueryWapper geoDistance(String field, String distance, Object lon, Object lat) {
        Map<String, Object> geo = new HashMap<>(initialCapacity);
        geo.put("distance", distance);
        Map<String, Object> geoLocation = new HashMap<>(initialCapacity);
        geoLocation.put("lat", lat);
        geoLocation.put("lon", lon);
        geo.put(field, geoLocation);
        return put("geo_distance", geo);
    }

    public static QueryWapper filter(QueryWapper value) {
        return put("filter", value.resultMapper);
    }

    public enum RangeEnum {

        lt("lt"), gt("gt"), lte("lte"), gte("gte");

        RangeEnum(String rangeName) {
            this.rangeName = rangeName;
        }

        private String rangeName;

        public String getRangeName() {
            return rangeName;
        }

    }

    public static QueryWapper rangeLte(String filed, Object value) {
        Map<String, Object> lte = new HashMap<>(initialCapacity);
        lte.put("lte", value);
        Map<String, Object> filedMap = new HashMap<>(initialCapacity);
        filedMap.put(filed, lte);
        return put("range", filedMap);
    }

    public static QueryWapper rangeGte(String filed, Object value) {
        Map<String, Object> gte = new HashMap<>(initialCapacity);
        gte.put("gte", value);
        Map<String, Object> filedMap = new HashMap<>(initialCapacity);
        filedMap.put(filed, gte);
        return put("range", filedMap);
    }

    public static QueryWapper rangeLt(String filed, Object value) {
        Map<String, Object> lt = new HashMap<>(initialCapacity);
        lt.put("lt", value);
        Map<String, Object> filedMap = new HashMap<>(initialCapacity);
        filedMap.put(filed, lt);
        return put("range", filedMap);
    }

    public static QueryWapper rangeGt(String filed, Object value) {
        Map<String, Object> gt = new HashMap<>(initialCapacity);
        gt.put("gt", value);
        Map<String, Object> filedMap = new HashMap<>(initialCapacity);
        filedMap.put(filed, gt);
        return put("range", filedMap);
    }

    public static QueryWapper range(String filed, RangeEnum minRange, Object minValue, RangeEnum maxRange, Object maxValue) {
        Map<String, Object> ranges = new HashMap<>(initialCapacity);
        ranges.put(maxRange.getRangeName(), maxValue);
        ranges.put(minRange.getRangeName(), minValue);
        Map<String, Object> filedMap = new HashMap<>(initialCapacity);
        filedMap.put(filed, ranges);
        return put("range", filedMap);
    }


    public static QueryWapper bool(QueryWapper value) {
        return put("bool", value.resultMapper);
    }

    public static QueryWapper query(QueryWapper value) {
        return put("query", value.resultMapper);
    }

    public QueryWapper from(int from) {
        this.resultMapper.put("from", from);
        return this;
    }

    public QueryWapper size(int size) {
        this.resultMapper.put("size", size);
        return this;
    }

    public QueryWapper sort(Map<String, String> sortMap) {
        List<Map<String, String>> sortList = new ArrayList<>();
        sortMap.forEach((k,v) -> {
            Map<String,String> sort = new HashMap<>(3);
            sort.put(k,v);
            sortList.add(sort);
        });
        this.resultMapper.put("sort", sortList);
        return this;
    }

    public Map<String, Object> getResultMapper() {
        return resultMapper;
    }
}
